package com.auth.manager.security.auth;

import cn.hutool.core.util.StrUtil;
import com.auth.manager.security.config.WhiteUrlConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @version 1.0.0
 * @Date: 2022/4/27 16:49
 * @Author ZhuYouBin
 * @Description [用户授权],主要作用是: 获取当前用户访问的url,从[AuthMetaSource]里面的映射找到需要的角色, 判断当前用户是否拥有访问url的角色
 */
@Component
public class AuthDecisionManager implements AccessDecisionManager {

//    @Autowired
//    private WhiteUrlConfig whiteUrl;

    // 这个方法用于判断当前用户是否具有访问当前url的权限
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
        /**
         * 这个方法主要处理逻辑是什么呢？？？
         * 1、放行白名单url, 不处理
         * 2、遍历角色集合，如果角色集合中, 存在【ROLE_login】我们之前设置的默认角色标识, 说明用户访问的url是不存在的, 则抛出异常即可
         * 3、获取当前用户所拥有的所有角色集合, 判断访问当前url所需角色是否在用户拥有的角色集合里面
         * 4、如果存在访问的角色, 则可以结束方法, 表示当前用户拥有访问当前url的权限
         * 5、否则, 抛出异常, 表示当前用户没有访问url的权限
         */
        // 1、白名单url不需要进行权限判断
        FilterInvocation request = (FilterInvocation) object;
        String requestUrl = request.getRequestUrl();

        // TODO 获取到白名单url, 然后判断当前url是否存在白名单里面
        List<String> whiteUrlLists = WhiteUrlConfig.getWhiteUrl();
        if (!CollectionUtils.isEmpty(whiteUrlLists)) {
            for (String whiteUrl : whiteUrlLists) {
                if (StrUtil.contains(requestUrl, whiteUrl)) {
                    return; // 结束执行,白名单url不进行权限判断
                }
            }
        }

        // 2、遍历角色集合
        for (ConfigAttribute attribute : configAttributes) {
            // 3、判断当前角色是否等于【ROLE_login】默认标识
            if ("ROLE_login".equals(attribute.getAttribute())) {
                break;
            }
            // 3、判断当前用户拥有的角色是否包含在url访问角色里面
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            for (GrantedAuthority authority : authorities) {
                if (attribute.getAttribute().equals(authority.getAuthority())) {
                    return; // 说明当前用户拥有权限访问url
                }
            }
        }

        // 5、抛出异常, 当前用户没有访问权限
        throw new AccessDeniedException("权限不足，请联系管理员！");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }
}
